import java.io.File;
import java.util.Random;

import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.trees.J48;
import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.TextDirectoryLoader;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;


public class Learner {

;
	
	//o filtro usado pra gerar a contagem de palavras
	private StringToWordVector filtro = new StringToWordVector();
	
	//o classificador
	private Classifier classificador = new J48();
	
	//se o modelo estah atualizado
	private boolean uptodate;
	
	private Instances treinoFiltrado;
	private Instances treinoRaw;
	private Instances unlabeledRaw;
	private Instances teste;
	
	private double maior;
	double menor;
	public boolean parar;
	int adicionado;
	private double treeshold = 0.9;
	
	
	//constroi um dataset de treinamento 
	public void createDataset(String diretorio) throws Exception{
		
		TextDirectoryLoader loader = new TextDirectoryLoader();
		loader.setDirectory(new File(diretorio));
		treinoRaw = loader.getDataSet();
		
		filtro = new StringToWordVector();
		filtro.setInputFormat(treinoRaw);
		
		Instances dataSetFiltrado = Filter.useFilter(treinoRaw, filtro);
		treinoFiltrado = dataSetFiltrado;
		classificador.buildClassifier(treinoFiltrado);
	}
	
	public void reconstruirClassificador() throws Exception{
		/*filtro = new StringToWordVector();
		filtro.setInputFormat(treinoRaw);
		treinoFiltrado = Filter.useFilter(treinoRaw, filtro);*/
		classificador.buildClassifier(treinoFiltrado);
	}
	
	public void createDatasetUnlabeled(String diretorio) throws Exception{
		
		TextDirectoryLoader loader = new TextDirectoryLoader();
		loader.setDirectory(new File(diretorio));
		unlabeledRaw = loader.getDataSet();
	}
	
	/*
	 * Recebe uma instancia do dataRaw e adiciona ao conjunto treino inicial
	 */
	public void updateData(Instance instance){
		treinoRaw.add(instance);
		uptodate = false;
	}
	
	
	public void classificar(int index) throws Exception{
		Instance instanciaRaw = unlabeledRaw.instance(index);
		filtro.input(instanciaRaw);
		Instance filteredInstance = filtro.output();
		
		double predicted = classificador.classifyInstance(filteredInstance);
		double[] arranjo = classificador.distributionForInstance(filteredInstance);
		
		if(arranjo[0] > treeshold || arranjo[1] > treeshold){
			double var = 0;
			parar = false;
			if(arranjo[0] > arranjo[1]){
				var = arranjo[0];
			}else{
				var = arranjo[1];
			}
			if(var > maior){
				maior = var;
			}else if(var < menor){
				menor = var;
			}
			adicionado++;
			
			instanciaRaw.setClassValue(predicted);
			instanciaRaw.setDataset(treinoRaw);
			filteredInstance.setClassValue(predicted);
			treinoFiltrado.add(filteredInstance);
			
			//updateData(instanciaRaw);
			unlabeledRaw.delete(index);
		}

	}
	
	public float testar(Instances data){
		float resultado;
		int tamanho = data.numInstances();
		int acertos = 0;
		
		for(int i = 0; i < tamanho; i++){
			double predicted = 0;
			try {
				predicted = classificador.classifyInstance(data.instance(i));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			double corrected = data.instance(i).classValue();
			if(corrected == predicted){
				acertos++;
			}
		}
		
		resultado = acertos/tamanho;
		return resultado;
		
	}
	
	public static void main(String[] args){
		try{
			String diretorioTreino = args[0];
			String diretorioUnlabeled = args[1];
			
			Learner learner = new Learner();
			System.out.println("Criando treino...");
			learner.createDataset(diretorioTreino);
			System.out.println("Numero de instnacias labeled  " + learner.treinoFiltrado.numInstances());
			System.out.println("Importando unlabeled data");
			learner.createDatasetUnlabeled(diretorioUnlabeled);
			
			Instances unlabeledCopy = new Instances(learner.unlabeledRaw);
			
			System.out.println("Classificando");
			System.out.println(learner.unlabeledRaw.numInstances());
			learner.parar = false;
			
			int folds = 10;
			int seed = 667;
			Random rand = new Random(seed);
			Instances ranData = new Instances(learner.treinoFiltrado);
			ranData.randomize(rand);
			ranData.stratify(folds);
			while(learner.treeshold > 0.6){
				System.out.println("Treeshold " + learner.treeshold);
				Evaluation eval = new Evaluation(ranData);
				for(int n = 0; n < folds; n++){
					learner.parar = false;
					System.out.println("Fold " + n + " as testset ");
					learner.unlabeledRaw = new Instances(unlabeledCopy);
					learner.treinoFiltrado = ranData.trainCV(folds, n);
					Instances teste = ranData.testCV(folds, n);
					while(learner.parar == false){
						learner.parar = true;
						learner.maior = 0;
						learner.menor = 1;
						learner.adicionado = 0;
						for(int i = 0; i < learner.unlabeledRaw.numInstances(); i++){
							learner.classificar(i);			
						}
						
						System.out.println("Instancias adicionadas: " + learner.adicionado + " Maior confianca: " + learner.maior + " Menor confianca: " + learner.menor);
						learner.reconstruirClassificador();
					}
					eval.evaluateModel(learner.classificador, teste);
				}
			    System.out.println(eval.toSummaryString("=== " + folds + "-fold Cross-validation ===", false));
			    learner.treeshold = learner.treeshold - 0.1;
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
